/*
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
 */
package erwiki.part.rssgenerator.internal.services;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.elwiki_data.PageContent;
import org.elwiki_data.WikiPage;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ServiceScope;
import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;

import erwiki.WatchDog;
import erwiki.api.attachment.AttachmentManager;
import erwiki.api.auth.AuthorizationManager;
import erwiki.api.auth.ISessionMonitor;
import erwiki.api.core.Engine;
import erwiki.api.pages.PageManager;
import erwiki.api.plugin.IWeblogPlugin;
import erwiki.api.plugin.PluginManager;
import erwiki.api.rss.RssGenerator;
import erwiki.api.rwc.NewContext;
import erwiki.api.rwc.RWC;
import erwiki.api.rwc.engune.RwcServlet;
import erwiki.api.ui.PageCommand;
import erwiki.api.ui.progress.ProgressManager;
import erwiki.configuration.IWikiConfiguration;
import erwiki.util.HttpUtil;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

/**
 * Это сервлет управления RSS.
 * <p>
 * Параметры запроса:
 * <ul>
 * <li>pageid - идентификатор страницы.</li>
 * <li>mode - определяет режим новостной ленты: blog|wiki. По умолчанию - blog.</li>
 * <li>type - определяет тип новостной ленты: rss10|rss20|atom. По умолчанию - rss20.</li>
 * </ul>
 */
//@formatter:off
@Component(
  service=Servlet.class,
  property= {
  	HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN + "=/rss/*",
  },
  scope=ServiceScope.PROTOTYPE,
  name="web.RssServlet")
//@formatter:on
public class RssServlet extends RwcServlet {

	private static final long serialVersionUID = 1L;

	private static final Logger log = Logger.getLogger(RssServlet.class);

	private CacheManager m_cacheManager = CacheManager.getInstance();
	private final static String cacheName = "jspwiki.rssCache"; //:FVK: rename the "jspwiki".
	private Cache m_rssCache;
	private int m_expiryPeriod = 24 * 60 * 60;
	private int cacheCapacity = 1000;

	/** Stores configuration. */
	@Reference
	private IWikiConfiguration wikiConfiguration;

	@Reference
	private Engine m_engine;

	@Reference
	private ISessionMonitor sessionMonitor;

	@Reference
	private AuthorizationManager authorizationManager;

	@Reference
	RssGenerator rssGenerator;

	@Reference
	private AttachmentManager attachmentManager;

	@Reference
	private ProgressManager progressManager;

	@Reference
	private PageManager pageManager;

	@Reference
	private PluginManager pluginManager;
	
	@Activate
	protected void startup() {
		log.debug("«web» start " + RssServlet.class.getSimpleName());

		if (m_cacheManager.cacheExists(cacheName)) {
			m_rssCache = m_cacheManager.getCache(cacheName);
		} else {
			log.info("cache with name " + cacheName + " not found in ehcache.xml, creating it with defaults.");
			m_rssCache = new Cache(cacheName, cacheCapacity, false, false, m_expiryPeriod, m_expiryPeriod);
			m_cacheManager.addCache(m_rssCache);
		}
	}

	@Override
	protected Engine getEngine() {
		return m_engine;
	}

	@Override
	protected ISessionMonitor getSessionMonitor() {
		return sessionMonitor;
	}

	/**
	 * Initializes the servlet from Engine properties.
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);

		// String allowed = wikiConfiguration.getStringProperty(AttachmentManager.PROP_ALLOWEDEXTENSIONS,
		// null);
		// m_maxSize = wikiConfiguration.getIntegerProperty(AttachmentManager.PROP_MAXSIZE,
		// Integer.MAX_VALUE);
	}

	/**
	 * Для тестирования APO-переключения контекста.
	 * 
	 * @param cs
	 * @param str
	 */
	public void testContextSwitch(NewContext cs, String str) {
		System.out.println("To Do with new context: " + cs);// :FVK:
	}

	/**
	 * GET сервлета. Параметры запроса: pageid, mode, type.</br>
	 * Параметры mode, type - не обязательны.
	 */
	@Override
	// @LoggedMethod
	protected void serviceDoGet(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		log.debug("START serviceDoGet.");
		System.out.println("-- enter in RssServlet.serviceDoGet");// :FVK:
		// Указать предназначение контекста.
		RWC.INSTANCE.setCommand(PageCommand.RSS);

		testContextSwitch(new NewContext(PageCommand.VIEW), "test");

		// :FVK:
		// testFile(request, response); if (1 == 1) return;

		// Вывести ошибку, если генерация RSS не включена.
		if (!rssGenerator.isEnabled()) {
			response.sendError(HttpServletResponse.SC_NOT_FOUND, "RSS feeds are disabled at administrator request.");
			return;
		}

		WatchDog watchDog = null;
		try {
			/* Разбор параметров URL.
			 */
			String pageId = request.getParameter("pageid");
			if (StringUtils.isBlank(pageId)) {
				response.sendError(HttpServletResponse.SC_NOT_FOUND, "Page id is not specified");
				return;
			}
			/* TODO: реализовать проверку допуска к заданной странице...
			if (authorizationManager.hasAccess(wikiContext, response)) {
				return;
			}
			*/

			watchDog = WatchDog.getCurrentWatchDog(m_engine);
			watchDog.enterState("Generating RSS", 60);

			WikiPage wikipage = pageManager.getPageById(pageId);
			if (wikipage == null) {
				response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such page with id: " + pageId);
				return;
			}
			RWC.INSTANCE.setPage(wikipage);

			String mode = request.getParameter("mode");
			String type = request.getParameter("type");
			if (mode == null || !(mode.equals(RssGenerator.MODE_BLOG) || mode.equals(RssGenerator.MODE_WIKI))) {
				mode = RssGenerator.MODE_BLOG;
			}
			if (type == null || !(type.equals(RssGenerator.RSS10) //
					|| type.equals(RssGenerator.RSS20) || type.equals(RssGenerator.ATOM))) {
				type = RssGenerator.RSS20;
			}

			// Установка формата: mime-тип контента, и содержимого ответа.
			response.setContentType(RssGenerator.getContentType(type) + "; charset=UTF-8");

			StringBuffer result = new StringBuffer();
			SimpleDateFormat iso8601fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

			// TODO: параметры RSS генератора - поместить в Preferences.
			// Properties properties = m_engine.getWikiConfiguration().getWikiPreferences();
			String channelDescription = ""; // default :FVK: TextUtil.getRequiredProperty( properties,
											// RssGenerator.PROP_CHANNEL_DESCRIPTION );
			String channelLanguage = "en-us"; // default :FVK: TextUtil.getRequiredProperty( properties,
												// RssGenerator.PROP_CHANNEL_LANGUAGE );

			/* Теперь перечислим элементы (блог / версии контекста страницы).
			 */
			List<PageContent> changed;

			if (RssGenerator.MODE_BLOG.equals(mode)) {
				IWeblogPlugin plug = (IWeblogPlugin) pluginManager.getWikiPlugin("WeblogPlugin");
				changed = plug.findBlogEntries(wikipage.getName(), new Date(0L), new Date());
			} else {
				changed = wikipage.getPageContents();
			}

			/* Проверить, и если ничего не изменилось, то просто вернуть 304.
			 */
			boolean hasChanged = false;
			Date latest = new Date(0);
			for (PageContent content : changed) {
				if (!HttpUtil.checkFor304(request, content.getWikipage().getId(), content.getCreationDate())) {
					hasChanged = true;
				}
				if (content.getCreationDate().after(latest)) {
					latest = content.getCreationDate();
				}
			}

			if (!hasChanged && changed.size() > 0) {
				response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
				return;
			}

			response.addDateHeader("Last-Modified", latest.getTime());
			response.addHeader("ETag", HttpUtil.createETag(wikipage.getName(), wikipage.getLastModifiedDate()));

			/* Попробовать получить XML RSS из кеша.
			 * Создаем хэш-ключ на основе LastModified-даты, поэтому всякий раз, когда она меняется,
			 * меняется и хэш-ключ, поэтому нам не нужно вносить какие-либо специальные изменения.
			 * 
			 * TODO: Выяснить, будет ли здесь хорошей идеей использовать disk-based кеш.
			 */
			String hashKey = RWC.INSTANCE.getPage().getId() + ";" + mode + ";" + type + ";" + latest.getTime();

			String rss = "";

			Element element = m_rssCache.get(hashKey);
			if (element != null) {
				rss = (String) element.getObjectValue();
			} else {
				rss = rssGenerator.generateFeed(changed, mode, type);
				m_rssCache.put(new Element(hashKey, rss));
			}

			PrintWriter out = response.getWriter();
			out.write("""
					<?xml version="1.0" encoding="UTF-8"?>
					""");
			out.println(rss);

			// :FVK: может надо добавить? -- response.setStatus(HttpServletResponse.SC_OK);
		} catch (Exception e) {
			e.printStackTrace(); // TODO: remove this line.
			response.sendError(HttpServletResponse.SC_NOT_FOUND, "Something went wrong: " + e.getMessage());
		} finally {
			if (watchDog != null)
				watchDog.exitState();
		}
	}

	@Deprecated
	private void testFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
		String pageName = "Main";
		Date latest = new Date();
		Date lastModifiedDate = new Date();

		String mimetype = "application/rss+xml";
		response.setContentType(mimetype + "; charset=UTF-8");
		response.addDateHeader("Last-Modified", latest.getTime());
		response.addHeader("ETag", HttpUtil.createETag(pageName, lastModifiedDate));

		String filePath = "/home/vfedorov/dev/dev_erwiki/TEMP/_RDF_RSS/main.rss";
		File f = new File(filePath);
		try (InputStream in = new FileInputStream(f); OutputStream out = response.getOutputStream()) {
			int read;
			byte[] buffer = new byte[10000];
			while ((read = in.read(buffer)) > -1) {
				out.write(buffer, 0, read);
			}
		}
	}

}
